/*
 *
 */
package com.ePadink;


class DCT
{
  public int N = 8;

  public int QUALITY = 80;

  public Object[] quantum = new Object[2];
  public Object[] Divisors = new Object[2];

  public int[] quantum_luminance = new int[this.N * this.N];
  public double[] DivisorsLuminance = new double[this.N * this.N];

  public int[] quantum_chrominance = new int[this.N * this.N];
  public double[] DivisorsChrominance = new double[this.N * this.N];

  public DCT(int QUALITY)
  {
    initMatrix(QUALITY);
  }

  private void initMatrix(int quality)
  {
    double[] AANscaleFactor = { 1.0D, 1.387039845D, 1.306562965D, 1.175875602D, 1.0D, 0.785694958D, 0.5411961D, 0.275899379D };

    int Quality = quality;
    if (Quality <= 0)
      Quality = 1;
    if (Quality > 100)
      Quality = 100;
    if (Quality < 50)
      Quality = 5000 / Quality;
    else {
      Quality = 200 - Quality * 2;
    }

    this.quantum_luminance[0] = 16;
    this.quantum_luminance[1] = 11;
    this.quantum_luminance[2] = 10;
    this.quantum_luminance[3] = 16;
    this.quantum_luminance[4] = 24;
    this.quantum_luminance[5] = 40;
    this.quantum_luminance[6] = 51;
    this.quantum_luminance[7] = 61;
    this.quantum_luminance[8] = 12;
    this.quantum_luminance[9] = 12;
    this.quantum_luminance[10] = 14;
    this.quantum_luminance[11] = 19;
    this.quantum_luminance[12] = 26;
    this.quantum_luminance[13] = 58;
    this.quantum_luminance[14] = 60;
    this.quantum_luminance[15] = 55;
    this.quantum_luminance[16] = 14;
    this.quantum_luminance[17] = 13;
    this.quantum_luminance[18] = 16;
    this.quantum_luminance[19] = 24;
    this.quantum_luminance[20] = 40;
    this.quantum_luminance[21] = 57;
    this.quantum_luminance[22] = 69;
    this.quantum_luminance[23] = 56;
    this.quantum_luminance[24] = 14;
    this.quantum_luminance[25] = 17;
    this.quantum_luminance[26] = 22;
    this.quantum_luminance[27] = 29;
    this.quantum_luminance[28] = 51;
    this.quantum_luminance[29] = 87;
    this.quantum_luminance[30] = 80;
    this.quantum_luminance[31] = 62;
    this.quantum_luminance[32] = 18;
    this.quantum_luminance[33] = 22;
    this.quantum_luminance[34] = 37;
    this.quantum_luminance[35] = 56;
    this.quantum_luminance[36] = 68;
    this.quantum_luminance[37] = 109;
    this.quantum_luminance[38] = 103;
    this.quantum_luminance[39] = 77;
    this.quantum_luminance[40] = 24;
    this.quantum_luminance[41] = 35;
    this.quantum_luminance[42] = 55;
    this.quantum_luminance[43] = 64;
    this.quantum_luminance[44] = 81;
    this.quantum_luminance[45] = 104;
    this.quantum_luminance[46] = 113;
    this.quantum_luminance[47] = 92;
    this.quantum_luminance[48] = 49;
    this.quantum_luminance[49] = 64;
    this.quantum_luminance[50] = 78;
    this.quantum_luminance[51] = 87;
    this.quantum_luminance[52] = 103;
    this.quantum_luminance[53] = 121;
    this.quantum_luminance[54] = 120;
    this.quantum_luminance[55] = 101;
    this.quantum_luminance[56] = 72;
    this.quantum_luminance[57] = 92;
    this.quantum_luminance[58] = 95;
    this.quantum_luminance[59] = 98;
    this.quantum_luminance[60] = 112;
    this.quantum_luminance[61] = 100;
    this.quantum_luminance[62] = 103;
    this.quantum_luminance[63] = 99;

    for (int j = 0; j < 64; j++)
    {
      int temp = (this.quantum_luminance[j] * Quality + 50) / 100;
      if (temp <= 0) temp = 1;
      if (temp > 255) temp = 255;
      this.quantum_luminance[j] = temp;
    }
    int index = 0;
    for (int i = 0; i < 8; i++) {
      for (int j = 0; j < 8; j++)
      {
        this.DivisorsLuminance[index] = (1.0D / (this.quantum_luminance[index] * AANscaleFactor[i] * AANscaleFactor[j] * 8.0D));
        index++;
      }

    }

    this.quantum_chrominance[0] = 17;
    this.quantum_chrominance[1] = 18;
    this.quantum_chrominance[2] = 24;
    this.quantum_chrominance[3] = 47;
    this.quantum_chrominance[4] = 99;
    this.quantum_chrominance[5] = 99;
    this.quantum_chrominance[6] = 99;
    this.quantum_chrominance[7] = 99;
    this.quantum_chrominance[8] = 18;
    this.quantum_chrominance[9] = 21;
    this.quantum_chrominance[10] = 26;
    this.quantum_chrominance[11] = 66;
    this.quantum_chrominance[12] = 99;
    this.quantum_chrominance[13] = 99;
    this.quantum_chrominance[14] = 99;
    this.quantum_chrominance[15] = 99;
    this.quantum_chrominance[16] = 24;
    this.quantum_chrominance[17] = 26;
    this.quantum_chrominance[18] = 56;
    this.quantum_chrominance[19] = 99;
    this.quantum_chrominance[20] = 99;
    this.quantum_chrominance[21] = 99;
    this.quantum_chrominance[22] = 99;
    this.quantum_chrominance[23] = 99;
    this.quantum_chrominance[24] = 47;
    this.quantum_chrominance[25] = 66;
    this.quantum_chrominance[26] = 99;
    this.quantum_chrominance[27] = 99;
    this.quantum_chrominance[28] = 99;
    this.quantum_chrominance[29] = 99;
    this.quantum_chrominance[30] = 99;
    this.quantum_chrominance[31] = 99;
    this.quantum_chrominance[32] = 99;
    this.quantum_chrominance[33] = 99;
    this.quantum_chrominance[34] = 99;
    this.quantum_chrominance[35] = 99;
    this.quantum_chrominance[36] = 99;
    this.quantum_chrominance[37] = 99;
    this.quantum_chrominance[38] = 99;
    this.quantum_chrominance[39] = 99;
    this.quantum_chrominance[40] = 99;
    this.quantum_chrominance[41] = 99;
    this.quantum_chrominance[42] = 99;
    this.quantum_chrominance[43] = 99;
    this.quantum_chrominance[44] = 99;
    this.quantum_chrominance[45] = 99;
    this.quantum_chrominance[46] = 99;
    this.quantum_chrominance[47] = 99;
    this.quantum_chrominance[48] = 99;
    this.quantum_chrominance[49] = 99;
    this.quantum_chrominance[50] = 99;
    this.quantum_chrominance[51] = 99;
    this.quantum_chrominance[52] = 99;
    this.quantum_chrominance[53] = 99;
    this.quantum_chrominance[54] = 99;
    this.quantum_chrominance[55] = 99;
    this.quantum_chrominance[56] = 99;
    this.quantum_chrominance[57] = 99;
    this.quantum_chrominance[58] = 99;
    this.quantum_chrominance[59] = 99;
    this.quantum_chrominance[60] = 99;
    this.quantum_chrominance[61] = 99;
    this.quantum_chrominance[62] = 99;
    this.quantum_chrominance[63] = 99;

    for (int j = 0; j < 64; j++)
    {
      int temp = (this.quantum_chrominance[j] * Quality + 50) / 100;
      if (temp <= 0) temp = 1;
      if (temp >= 255) temp = 255;
      this.quantum_chrominance[j] = temp;
    }
    index = 0;
    for (int i = 0; i < 8; i++) {
      for (int j = 0; j < 8; j++)
      {
        this.DivisorsChrominance[index] = (1.0D / (this.quantum_chrominance[index] * AANscaleFactor[i] * AANscaleFactor[j] * 8.0D));
        index++;
      }

    }

    this.quantum[0] = this.quantum_luminance;
    this.Divisors[0] = this.DivisorsLuminance;
    this.quantum[1] = this.quantum_chrominance;
    this.Divisors[1] = this.DivisorsChrominance;
  }

  public double[][] forwardDCTExtreme(float[][] input)
  {
    double[][] output = new double[this.N][this.N];

    for (int v = 0; v < 8; v++) {
      for (int u = 0; u < 8; u++) {
        for (int x = 0; x < 8; x++) {
          for (int y = 0; y < 8; y++) {
            output[v][u] += input[x][y] * Math.cos((2 * x + 1) * u * 3.141592653589793D / 16.0D) * Math.cos((2 * y + 1) * v * 3.141592653589793D / 16.0D);
          }
        }
        output[v][u] *= 0.25D * (u == 0 ? 1.0D / Math.sqrt(2.0D) : 1.0D) * (v == 0 ? 1.0D / Math.sqrt(2.0D) : 1.0D);
      }
    }
    return output;
  }

  public double[][] forwardDCT(float[][] input)
  {
    double[][] output = new double[this.N][this.N];

    for (int i = 0; i < 8; i++) {
      for (int j = 0; j < 8; j++) {
        output[i][j] = (input[i][j] - 128.0D);
      }

    }

    for (int i = 0; i < 8; i++) {
      double tmp0 = output[i][0] + output[i][7];
      double tmp7 = output[i][0] - output[i][7];
      double tmp1 = output[i][1] + output[i][6];
      double tmp6 = output[i][1] - output[i][6];
      double tmp2 = output[i][2] + output[i][5];
      double tmp5 = output[i][2] - output[i][5];
      double tmp3 = output[i][3] + output[i][4];
      double tmp4 = output[i][3] - output[i][4];

      double tmp10 = tmp0 + tmp3;
      double tmp13 = tmp0 - tmp3;
      double tmp11 = tmp1 + tmp2;
      double tmp12 = tmp1 - tmp2;

      output[i][0] = (tmp10 + tmp11);
      output[i][4] = (tmp10 - tmp11);

      double z1 = (tmp12 + tmp13) * 0.707106781D;
      output[i][2] = (tmp13 + z1);
      output[i][6] = (tmp13 - z1);

      tmp10 = tmp4 + tmp5;
      tmp11 = tmp5 + tmp6;
      tmp12 = tmp6 + tmp7;

      double z5 = (tmp10 - tmp12) * 0.382683433D;
      double z2 = 0.5411961D * tmp10 + z5;
      double z4 = 1.306562965D * tmp12 + z5;
      double z3 = tmp11 * 0.707106781D;

      double z11 = tmp7 + z3;
      double z13 = tmp7 - z3;

      output[i][5] = (z13 + z2);
      output[i][3] = (z13 - z2);
      output[i][1] = (z11 + z4);
      output[i][7] = (z11 - z4);
    }

    for (int i = 0; i < 8; i++) {
      double tmp0 = output[0][i] + output[7][i];
      double tmp7 = output[0][i] - output[7][i];
      double tmp1 = output[1][i] + output[6][i];
      double tmp6 = output[1][i] - output[6][i];
      double tmp2 = output[2][i] + output[5][i];
      double tmp5 = output[2][i] - output[5][i];
      double tmp3 = output[3][i] + output[4][i];
      double tmp4 = output[3][i] - output[4][i];

      double tmp10 = tmp0 + tmp3;
      double tmp13 = tmp0 - tmp3;
      double tmp11 = tmp1 + tmp2;
      double tmp12 = tmp1 - tmp2;

      output[0][i] = (tmp10 + tmp11);
      output[4][i] = (tmp10 - tmp11);

      double z1 = (tmp12 + tmp13) * 0.707106781D;
      output[2][i] = (tmp13 + z1);
      output[6][i] = (tmp13 - z1);

      tmp10 = tmp4 + tmp5;
      tmp11 = tmp5 + tmp6;
      tmp12 = tmp6 + tmp7;

      double z5 = (tmp10 - tmp12) * 0.382683433D;
      double z2 = 0.5411961D * tmp10 + z5;
      double z4 = 1.306562965D * tmp12 + z5;
      double z3 = tmp11 * 0.707106781D;

      double z11 = tmp7 + z3;
      double z13 = tmp7 - z3;

      output[5][i] = (z13 + z2);
      output[3][i] = (z13 - z2);
      output[1][i] = (z11 + z4);
      output[7][i] = (z11 - z4);
    }

    return output;
  }

  public int[] quantizeBlock(double[][] inputData, int code)
  {
    int[] outputData = new int[this.N * this.N];

    int index = 0;
    for (int i = 0; i < 8; i++) {
      for (int j = 0; j < 8; j++)
      {
        outputData[index] = ((int)Math.round(inputData[i][j] * ((double[])(double[])this.Divisors[code])[index]));

        index++;
      }
    }

    return outputData;
  }

  public int[] quantizeBlockExtreme(double[][] inputData, int code)
  {
    int[] outputData = new int[this.N * this.N];

    int index = 0;
    for (int i = 0; i < 8; i++) {
      for (int j = 0; j < 8; j++) {
        outputData[index] = ((int)Math.round(inputData[i][j] / ((int[])(int[])this.quantum[code])[index]));
        index++;
      }
    }

    return outputData;
  }
}